package com.sunjy.common.aop;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.sunjy.common.annotation.BeanValid;
import com.sunjy.common.filter.InvokeContext;
import lombok.extern.slf4j.Slf4j;
import com.alibaba.fastjson.JSON;
import com.sunjy.common.filter.FilterAction;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Aspect
@Order(Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class BeanValidAspect {

    private Boolean beanFlag;



    private List<FilterAction> actions;

    private ThreadLocal<String> requestPath = new ThreadLocal<>(); // 请求地址
    private ThreadLocal<Object> inputParamMap = new ThreadLocal<>(); // 传入参数
    private ThreadLocal<Map<?, ?>> outputParamMap = new ThreadLocal<>(); // 存放输出结果
    private ThreadLocal<Long> startTimeMillis = new ThreadLocal<>(); // 开始时间
    private ThreadLocal<Long> endTimeMillis = new ThreadLocal<>(); // 结束时间
    private ThreadLocal<String> clientIp = new ThreadLocal<>(); // 客户端的ip



    @Around("@annotation(com.sunjy.common.annotation.BeanValid)")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {

        startTimeMillis.set(System.currentTimeMillis());
        MethodSignature ms = (MethodSignature) pjp.getSignature();
        Method method = ms .getMethod();
        Method realMethod = pjp.getTarget().getClass().getDeclaredMethod(ms.getName(), method.getParameterTypes());
        Annotation beanAnnotion = realMethod.getAnnotation(BeanValid.class);
        Boolean flag=false;
        if(null !=beanAnnotion){
            BeanValid beanValid = (BeanValid) beanAnnotion;
            flag=beanValid.flag();
        }
        Object rst = null;
        InvokeContext c = null;
        if ( flag && beanFlag) {
            try {
                RequestAttributes ra = RequestContextHolder.getRequestAttributes();
                if (ra == null) {
                    log.info("spring is not in web environment ");
                    return null;
                }
                ServletRequestAttributes r = (ServletRequestAttributes) ra;
                HttpServletRequest request = r.getRequest();
                HttpServletResponse response = r.getResponse();
                if("POST".equals(request.getMethod().toUpperCase())){
                    Object[] args=pjp.getArgs();
                    if(null !=args[0]){
                        inputParamMap.set(args[0]);
                    }
                }
                if("GET".equals(request.getMethod().toUpperCase())){
                    Map<?, ?> paras = request.getParameterMap();
                    inputParamMap.set(paras);
                }
                requestPath.set(request.getRequestURI());
                clientIp.set(getRemoteAddress(request));
                this.printIptLog();
                c = new InvokeContext<Object[]>(actions, response, request);
                c.setParam(pjp);
                boolean invokeResult = c.startInvoke();
                if (!invokeResult) {
                    return null;
                }
            } catch (Exception e) {
                log.error("Beadvalid获取请求参数出错->{}", e);
            }
        }
        try {
            rst = pjp.proceed();
        } finally {
            Map<String, Object> outputMap = new HashMap<>();
            outputMap.put("result", rst);
            outputParamMap.set(outputMap);
            endTimeMillis.set(System.currentTimeMillis());
            this.printOptLog();
            if (flag && beanFlag) {
                if (null != c) {
                    c.startPostInvoke(rst);
                }
            }
        }
        return rst;
    }
    private void printIptLog() {
        log.info("\n clientIp：" + clientIp.get()
                + "  url：" + requestPath.get()
                + "\n param：" + ( JSON.toJSON(inputParamMap.get())) + ";");
    }

    /**
     * 输出日志
     */
    private void printOptLog() {
        String endTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(startTimeMillis.get());
        log.info("\n clientIp：" + clientIp.get()
                + "  url：" + requestPath.get() + " pro_time："
                + (endTimeMillis.get() - startTimeMillis.get()) + "ms ;"
                + "\n result：" + JSON.toJSON(outputParamMap.get()));
    }

    public String getRemoteAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || ip.equalsIgnoreCase("unknown")) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || ip.equalsIgnoreCase("unknown")) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || ip.equalsIgnoreCase("unknown")) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
    public Boolean getBeanFlag() {
        return beanFlag;
    }

    public void setBeanFlag(Boolean beanFlag) {
        this.beanFlag = beanFlag;
    }
    public void setActions(List<FilterAction> actions) {
        this.actions = actions;
    }

}
